package secondriver.springsubway.example.rpv.validator;

import net.sf.oval.ConstraintViolation;
import net.sf.oval.guard.Guard;
import net.sf.oval.localization.message.ResourceBundleMessageResolver;
import org.apache.commons.beanutils.BeanUtils;
import secondriver.springsubway.example.rpv.ErrorDesc;
import secondriver.springsubway.example.rpv.ParameterValidator;
import secondriver.springsubway.example.rpv.ParameterValidatorResult;
import secondriver.springsubway.example.rpv.result.DefaultParameterValidatorResult;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

/**
 * Created by ZhangXiao on 2015/6/12.
 */
public abstract class AbstractParameterValidator implements ParameterValidator {

    protected Guard guard;

    private Map<Class, Object> cacheBeans = new HashMap<Class, Object>();

    private String messageBaseName = DEFAULT_MESSAGE_BASENAME;

    private boolean initStatus = false;

    @Override
    public final ParameterValidatorResult handleParameterValidator(Class parameterBeanClass, HttpServletRequest request) {
        this.preparedGuard();
        if (null == guard) {
            throw new NullPointerException("initGuard method must be instance Guard object.");
        }
        Object parameterBean = wrapperParameterBean(parameterBeanClass, request);
        if (null == parameterBean) {
            throw new RuntimeException(parameterBeanClass.getName() + " and request parameter cant create a object " +
                    "instance.");
        }
        List<ConstraintViolation> constraintViolationList = guard.validate(parameterBean);
        return wrapperConstraintViolation(constraintViolationList, parameterBean);
    }

    private void preparedGuard() {
        if (!isInitStatus()) {
            this.initMessageResourceBundle();
            this.initGuard();
        }
    }

    /**
     * 初始化Guard对象
     */
    public abstract void initGuard();

    /**
     * 初始化资源绑定
     */
    public void initMessageResourceBundle() {
        if (null != messageBaseName) {
            ResourceBundleMessageResolver messageResolver = (ResourceBundleMessageResolver) Guard.getMessageResolver();
            messageResolver.addMessageBundle(ResourceBundle.getBundle(messageBaseName, Locale.getDefault()));
        }
    }


    /**
     * 包装校验结果对象集
     *
     * @param constraintViolationList
     * @param parameterBean
     * @return
     */
    public ParameterValidatorResult wrapperConstraintViolation(List<ConstraintViolation> constraintViolationList, Object
            parameterBean) {
        List<ErrorDesc> valueList = new ArrayList<ErrorDesc>(constraintViolationList.size());
        for (ConstraintViolation cv : constraintViolationList) {
            valueList.add(new ErrorDesc(cv.getErrorCode(), cv.getMessage()));
        }
        return new DefaultParameterValidatorResult(valueList, parameterBean);
    }

    /**
     * 解析Request参数包装成指定参数类型的Bean对象
     *
     * @param parameterBeanClass
     * @param request
     * @return
     */
    public final Object wrapperParameterBean(Class parameterBeanClass, HttpServletRequest request) {
        Object bean = null;
        if (cacheBeans.containsKey(parameterBeanClass)) {
            bean = cacheBeans.get(parameterBeanClass);
        } else {
            try {
                bean = parameterBeanClass.newInstance();
                cacheBeans.put(parameterBeanClass, bean);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        Enumeration parameterNames = request.getParameterNames();
        HashMap<String, Object> map = new HashMap<String, Object>();
        while (parameterNames.hasMoreElements()) {
            String parameterName = (String) parameterNames.nextElement();
            map.put(parameterName, request.getParameterValues(parameterName));
        }
        try {
            BeanUtils.populate(bean, map);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return bean;
    }

    public String getMessageBaseName() {
        return messageBaseName;
    }

    public void setMessageBaseName(String messageBaseName) {
        this.messageBaseName = messageBaseName;
    }

    public boolean isInitStatus() {
        return initStatus;
    }
}